//
//  PIStream.m
//  playin
//
//  Created by playin on 2019/5/20.
//  Copyright © 2019 playin. All rights reserved.
//

#import "PIStream.h"
#import "PNSocket.h"
#import "PIUtil.h"

@interface PIStream () <PNSocketDelegate>

@end

enum PNStreamState {
    kSocketnull = 0, ///< 没有工作
    kConnecting = 1, ///<
    kConnected  = 2, ///< 已经连接socket
    kRegistering = 3, ///< 正在注册
    kregisterd  = 4 ///< 与服务器注册成功
};

@implementation PIStream
{
    PNSocket *_socket;
    NSString *_deviceName;
    NSString *_host;
    NSInteger _port;
    NSString *_token;
    int     _duration;
    
    int32_t _state;
    
    dispatch_queue_t _delegateQueue;
    dispatch_source_t _timer;
    NSError* _timerError;
}

- (instancetype)init {
    
    if (self = [super init]) {
        NSLog(@"[PIStream] init");
        _state = kSocketnull;
        _timer = nil;
        _delegateQueue = dispatch_queue_create("tech.playin.pistream.delegate", NULL);
        _socket = [[PNSocket alloc] initWithDelegate:self delegateQueue:_delegateQueue];
    }
    return self;
}


- (BOOL)startStreamWithToken:(nonnull NSString *)token deviceName:(nonnull NSString *)deviceName host:(nonnull NSString *)host port:(NSInteger)port duration:(uint)duration {
    
    if (![PIUtil validStr:token]) {
        NSLog(@"[PIStream] token invalid");
        return NO;
    }
    if (![PIUtil validStr:deviceName]) {
        NSLog(@"[PIStream] deviceName invalid");
        return NO;
    }
    if (![PIUtil validStr:host]) {
        NSLog(@"[PIStream] host invalid");
        return NO;
    }
    if (port <= 0 || port >= 65535) {
        NSLog(@"[PIStream] port invalid");
        return NO;
    }
    
    self->_host = host;
    self->_port = port;
    self->_token = token;
    self->_deviceName = deviceName;
    self->_duration = duration;
    
    NSError *error = nil;
    [_socket connectToHost:host onPort:port withTimeout:3 error:&error];
    if (error) {
        NSLog(@"failed to call connnectToHost. error:%@", error);
        return NO;
        
    }
    return YES;
}


- (void)sendTouchData:(NSData *)touchData {
    [_socket sendStreamWithStreamType:kPNPacketStreamTouch streamData:touchData];
}

- (void)stopStream {
    [_socket disconnect];
}


- (void)registerStream {

    NSMutableDictionary *params = [NSMutableDictionary new];
    [params setValue:self->_token forKey:@"token"];
    [params setValue:self->_deviceName forKey:@"device_name"];
    [params setValue:@1 forKey:@"os_type"];
    [params setValue:@"avcc" forKey:@"coder"];
    [self->_socket sendMessageWithPacketID:0 msgID:MSGID_REQ_USER_CONNECT data:params];
}

- (void)socketDidConnect:(PNSocket *)sock {
    
    NSLog(@"[PIStream] socketDidConnect");
    _state = kConnected;
    [self registerStream];
    
    _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _delegateQueue);
    dispatch_source_set_event_handler(_timer, ^{
        self->_timerError = [NSError errorWithDomain:@"pnstream" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"time out"}];
        [self stopStream];
    });
    dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self->_duration * NSEC_PER_SEC)), DISPATCH_TIME_FOREVER, 0);
    dispatch_resume(_timer);
}

- (void)socketDidDisconnect:(PNSocket *)sock withError:(nullable NSError *)err {
    
    NSLog(@"[PIStream] socketDidDisconnect err = %@", err);
    _state = kSocketnull;
    if (_timer) {
        dispatch_source_cancel(_timer);
        _timer = nil;
    }
    
    if (self.delegate && [self.delegate respondsToSelector:@selector(onPIStreamEnded:)]) {
        NSError* error = _timerError ? _timerError : err;
        [self.delegate onPIStreamEnded:error];
        _timerError = nil;
    }
}

- (void)socket:(PNSocket *)sock didReadPacket:(PNPacket *)packet {

    if (packet.packetType == kPNPacketTypeControl) {
        if (packet.messageID == MSGID_RES_USER_CONNECT) {
            int code = [[packet.messageBody valueForKey:@"code"] intValue];
            if (code == 0) {
                _state = kregisterd;
                [self->_socket sendStreamWithStreamType:kPNPacketStreamAndroidVideoStart streamData:[NSData new]];
                if (self.delegate && [self.delegate respondsToSelector:@selector(onPIStreamRegisterSuccess:)]) {
                    [self.delegate onPIStreamRegisterSuccess:packet.messageBody];
                }
            } else {
                NSError *registerError = [NSError errorWithDomain:@"pnstream" code:-1 userInfo:@{NSLocalizedDescriptionKey:[packet.messageBody valueForKey:@"error"]}];
                if (self.delegate && [self.delegate respondsToSelector:@selector(onPIStreamRegisterFailure:)]) {
                    [self.delegate onPIStreamRegisterFailure:registerError];
                }
                [self->_socket disconnect];
            }
            
        }
    } else if (packet.packetType == kPNPacketTypeStream) {
        
        if (packet.streamType == kPNPacketStreamH264 || packet.streamType == kPNPacketStreamAndroidVideoStart) {
                if (self.delegate && [self.delegate respondsToSelector:@selector(onPIStreamReceivedVideoData:)]) {
                    [self.delegate onPIStreamReceivedVideoData:packet.streamData];
                }
        } else if (packet.streamType == kPNPacketStreamPCM) {
                if (self.delegate && [self.delegate respondsToSelector:@selector(onPIStreamReceivedAudioData:)]) {
                    [self.delegate onPIStreamReceivedAudioData:packet.streamData];
                }
        } else if (packet.streamType == kPNPacketStreamH264WithTimestamp) {
                [_socket sendStreamWithStreamType:kPNPacketStreamTimestamp streamData:[packet.streamData subdataWithRange:NSMakeRange(0, 4)]];
               if (self.delegate && [self.delegate respondsToSelector:@selector(onPIStreamReceivedVideoData:)]) {
                   NSData *d = [packet.streamData subdataWithRange:NSMakeRange(4, packet.streamData.length-4)];
                   [self.delegate onPIStreamReceivedVideoData:d];
               }
        }
    }
}

@end
